﻿using Exiled.API.Features;
using Exiled.API.Features.Pools;
using Exiled.Events.EventArgs.Player;
using RgCore.GameData;
using RgCore.PlayerHud;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

namespace RgCore.Menu
{
    public enum MenuStatus : byte
    {
        Idle = 0,
        Busy = 1,
        Confirm = 2
    }
    public class MenuInterface
    {
        public MenuInterface(Player player)
        {
            Player = player;
            _builder = new HintBuilderSession(this);
            Dictionary<KeyCode, string> bindings = new Dictionary<KeyCode, string>()
            {
                {KeyCode.B, ".menu show"},
                {KeyCode.Backspace, ".menu back" },
                {KeyCode.UpArrow, ".menu up" },
                {KeyCode.DownArrow, ".menu down" },
                {KeyCode.Return, ".menu select 0"},
                {KeyCode.Plus, ".menu nextpage" },
                {KeyCode.Minus, ".menu prevpage" },
                {KeyCode.Keypad1, ".menu select 1" },
                {KeyCode.Keypad2, ".menu select 2" },
                {KeyCode.Keypad3, ".menu select 3" },
                {KeyCode.Keypad4, ".menu select 4" },
                {KeyCode.Keypad5, ".menu select 5" },
                {KeyCode.Keypad6, ".menu select 6" },
                {KeyCode.Keypad7, ".menu select 7" },
                {KeyCode.Keypad8, ".menu select 8" },
                {KeyCode.Keypad9, ".menu select 9" }
            };
            foreach (var bind in bindings)
            {
                player.ReferenceHub.characterClassManager.TargetChangeCmdBinding(bind.Key, bind.Value);
            }
        }

        public static List<MenuInterface> MenuInterfaces = new List<MenuInterface>();
        public static Thread Thread;
        public static readonly Page Root = new Page(null, "root", "游戏菜单", null, null, null, false);

        public static List<MenuElement> RegisteredElements { get; set; } = new List<MenuElement>() { Root };

        public DateTime LastOperation { get; set; } = DateTime.Now;
        public Page CurrentPage { get; private set; } = null;
        public Page PreviousPage { get; private set; } = null;
        public MenuStatus Status { get; set; } = MenuStatus.Idle;
        public int CurrentPointer { get; set; } = 1;
        public int CurrentPageIndex { get; set; } = 1;
        public Player Player { get; }
        private HintBuilderSession _builder;

        public static void SetEnabled()
        {
            Thread = new Thread(Update) { Name = "菜单系统" };
            Thread.Start();
            GameplayData.OnInitializedData += GameplayData_OnInitializedData;
            Exiled.Events.Handlers.Player.Destroying += Player_Destroying;
        }

        private static void GameplayData_OnInitializedData(Player player)
        {
            MenuInterfaces.Add(new MenuInterface(player));
        }

        public static void Player_Destroying(DestroyingEventArgs ev)
        {
            MenuInterfaces.RemoveAll(x => x.Player == ev.Player);
        }

        public static void Update()
        {
            while (true)
            {
                foreach (var mi in MenuInterfaces)
                {
                    if (mi.CurrentPage != null && mi.LastOperation.AddSeconds(10) < DateTime.Now)
                    {
                        mi.CurrentPage = null;
                    }
                }
                Thread.Sleep(1000);
            }
        }

        public void Activate()
        {
            ChangePage(Root);
        }
        public void Show()
        {
            if (CurrentPage == null)
                return;
            LastOperation = DateTime.Now;
            Player.DeleteMsg("menu", false);
            string[] a = _builder.GetString(CurrentPageIndex, CurrentPointer, true);
            Player.SendRawMsg("menu", a, 10, ScreenLocation.Center, true);
        }

        public void DownPointer()
        {
            if (Status.HasFlag(MenuStatus.Busy))
                return;
            if (CurrentPointer >= _builder.PagedCache[CurrentPageIndex - 1].Length)
            {
                NextPage();
            }
            else
            {
                CurrentPointer++;
                Show();
            }
        }

        public void UpPointer()
        {
            if (Status.HasFlag(MenuStatus.Busy))
                return;
            if (CurrentPointer == 1 || _builder.PagedCache[CurrentPageIndex - 1].Length == 0)
            {
                PrevPage();
            }
            else
            {
                CurrentPointer--;
                Show();
            }
        }

        public void Back()
        {
            Page tarPage = CurrentPage?.Parent;
            if (Status.HasFlag(MenuStatus.Confirm))
            {
                tarPage = tarPage ?? PreviousPage;
            }
            Status = MenuStatus.Idle;
            ChangePage(tarPage);
        }
        public void ShowConfirm(string[] message, Action<Player> confirm)
        {
            Status = MenuStatus.Confirm;
            Page confirmWindow = new Page(CurrentPage, "_confirmWindow", "确认", null, null, (_) =>
            {
                List<MenuElement> list = ListPool<MenuElement>.Pool.Get();
                list.AddRange(message.Select(x => new Text(null, "", () => x, false)));
                list.Add(new Option(null, "confirm", "确认", null, null, (player) =>
                {
                    confirm(player);
                    return string.Empty;
                }, false));
                return ListPool<MenuElement>.Pool.ToArrayReturn(list);
            }, false);
            ChangePage(confirmWindow);
        }
        public void SetBusy(bool left)
        {
            Status |= MenuStatus.Busy;
            StringBuilder pool = StringBuilderPool.Pool.Get();
            pool.AppendLine("<align=\"left\">%POS%[>><color=yellow><b>💿-- Loading --💿</b></color><<]");
            pool.AppendLine("%POS%<b><color=#00FFFF>正在加载中，请稍等片刻....</color></b>");
            pool.AppendLine("%POS%<b>------------[退格] 取消--------------</b></align>");
            pool = pool.Replace("%POS%", left ? "" : "<pos=25%>");
            string[] result = pool.ToString().Split('\n');
            StringBuilderPool.Pool.Return(pool);
            Player.DeleteMsg("menu", false);
            Player.SendRawMsg("menu", result, 10, ScreenLocation.Center, true);
        }
        public void ChangePage(Page newpage, bool skipLoad = false)
        {
            if (CurrentPage != newpage)
            {
                PreviousPage = CurrentPage;
                CurrentPage = newpage;
                CurrentPageIndex = 1;
                CurrentPointer = 1;
            }
            if (newpage == null)
            {
                Player.DeleteMsg("menu", true);
                return;
            }
            if (!skipLoad)
            {
                if (newpage.AwaitRequired)
                {
                    SetBusy(true);
                    _ = _builder.LoadPageAsync();
                    return;
                }
                _builder.LoadPage();
            }
            if (Status.HasFlag(MenuStatus.Busy) || (Status.HasFlag(MenuStatus.Confirm) && CurrentPage.ElementId != "_confirmWindow"))
                Status = MenuStatus.Idle;
            Show();
        }
        public void Select(int pointer)
        {
            if (Status.HasFlag(MenuStatus.Busy))
                return;
            if (CurrentPage == null)
                return;
            MenuElement[] mes = _builder.PagedCache[CurrentPageIndex - 1].Where(p => p is FunctionalElement).ToArray();
            if (pointer == 0)
                pointer = CurrentPointer;
            if (mes.Length == 0 || mes.Length < pointer)
                return;
            FunctionalElement fe = (FunctionalElement)mes.ElementAt(pointer - 1);
            if (fe.Disabled)
                return;
            bool result = fe.Condition(Player, out string response);
            if (response != string.Empty)
            {
                Player.SendFormattedMsg("menu_response", "<color=#54FF9F>回应</color>", response, 5,
                    result ? MsgType.Success_Response : MsgType.Failed_Response, ScreenLocation.CenterTop, true);
            }
            if (!result)
                return;
            if (fe is Page newpage)
            {
                ChangePage(newpage);
            }
            else if (fe is Option option)
            {
                string resp;
                Page current = Status.HasFlag(MenuStatus.Confirm) ? PreviousPage : CurrentPage;
                if (option.AwaitRequired)
                {
                    SetBusy(true);
                    Task.Run(delegate ()
                    {
                        resp = option.Trigger(Player);
                        if (resp != string.Empty)
                            Player.SendFormattedMsg("menu_response", "<color=#54FF9F>回应</color>", resp, 5,
                                    "<b><color=yellow>↩</color></b>", ScreenLocation.CenterTop, true);
                            ChangePage(current);
                    });
                }
                else
                {
                    resp = option.Trigger(Player);
                    if (resp != string.Empty)
                        Player.SendFormattedMsg("menu_response", "<color=#54FF9F>回应</color>", resp, 5,
                                "<b><color=yellow>↩</color></b>", ScreenLocation.CenterTop, true);
                    if (!option.DontRefresh)
                        ChangePage(current);
                }
            }
        }

        public void NextPage()
        {
            if (Status.HasFlag(MenuStatus.Busy))
                return;
            if (CurrentPage == null)
                return;
            if (CurrentPageIndex < _builder.PagedCache.Count)
            {
                CurrentPageIndex++;
                CurrentPointer = 1;
                Show();
            }
        }

        public void PrevPage()
        {
            if (Status.HasFlag(MenuStatus.Busy))
                return;
            if (CurrentPage == null)
                return;
            if (CurrentPageIndex > 1)
            {
                CurrentPageIndex--;
                CurrentPointer = 1;
                Show();
            }
        }
    }
}
